﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Model.Entities;
using log4net;
using HtmlAgilityPack;
using System.Linq;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;

namespace CardDownloader
{
    public class Utils
    {
        private static readonly ILog log = LogManager.GetLogger(typeof (Utils));

        public static CardInformation DownloadCard(string url, CardInformation cardInformation = null)
        {
            try
            {
                if(cardInformation == null)
                    cardInformation = new CardInformation();
                HtmlDocument document;
                string savedFileName = string.Format("Temp\\{0}.html", Path.GetFileNameWithoutExtension(url));
                if(!File.Exists(savedFileName))
                {
                    HtmlWeb web = new HtmlWeb();
                    document = web.Load(url);
                    if (!Directory.Exists("Temp"))
                        Directory.CreateDirectory("Temp");
                    File.WriteAllText(savedFileName, document.DocumentNode.InnerHtml);
                }
                else
                {
                    document = new HtmlDocument();
                    document.Load(savedFileName);
                }
                HtmlNode root = document.DocumentNode;

                string xpath = "//th[@class='cardtablerowheader' and contains(text(),'English')]/../td[@class='cardtablerowdata']";
                HtmlNode node = root.SelectSingleNode(xpath);
                if (node != null && string.IsNullOrEmpty(cardInformation.CardName))
                    cardInformation.CardName = node.InnerText.Trim();

                xpath = "//a[contains(text(),'Attribute')]/../../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.Attribute = node.InnerText.Trim();

                xpath = "//a[contains(text(),'Property')]/../../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.Property = node.InnerText.Trim();

                if(cardInformation.isMonster)
                {
                    xpath = "//a[contains(text(),'Type')]/../../td[@class='cardtablerowdata']";
                    node = root.SelectSingleNode(xpath);
                    if (node != null)
                        cardInformation.Types = node.InnerText.Trim().Split('/');
                }
                else
                {
                    xpath = "//th[@class='cardtablerowheader' and contains(text(),'Type')]/../td[@class='cardtablerowdata']";
                    node = root.SelectSingleNode(xpath);
                    if (node != null)
                        cardInformation.Types = node.InnerText.Trim().Split('/');
                }


                xpath = "//a[contains(text(),'Level')]/../../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.Level = int.Parse(node.InnerText.Trim());

                xpath = "//a[contains(text(),'Rank')]/../../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.Rank = int.Parse(node.InnerText.Trim());

                xpath = "//a[contains(text(),'ATK')]/../../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                {
                    int atk, def;
                    string atkDef = node.InnerText.Trim();
                    if (int.TryParse(atkDef.Split('/')[0], out atk))
                        cardInformation.Atk = atk;
                    if (int.TryParse(atkDef.Split('/')[1], out def))
                        cardInformation.Def = def;
                }

                xpath = "//th[@class='cardtablerowheader' and contains(text(),'Card Effect Types')]/../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.CardEffectTypes = node.InnerText.Trim().Split(',');

                xpath = "//b[contains(text(),'Card Descriptions')]/..//td[@class='navbox-list']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.CardDescription = node.InnerText.Trim();

                xpath = "//b[contains(text(),'Card Search Categories')]/..//th[@class='navbox-title']";
                HtmlNodeCollection nodeCollection = root.SelectNodes(xpath);
                if(nodeCollection != null)
                {
                    Dictionary<string, string[]> cardSearchCategories = nodeCollection.ToDictionary(item => item.InnerText.Trim(), item => new string[] { });
                    
                    xpath = "//b[contains(text(),'Card Search Categories')]/..//td[@class]";
                    nodeCollection = root.SelectNodes(xpath);
                    string[] keys = cardSearchCategories.Keys.ToArray();
                    if (nodeCollection != null)
                    {
                        for (int i = 0; i < keys.Length; i++)
                        {
                            if (nodeCollection[i] != null)
                                cardSearchCategories[keys[i]] = nodeCollection[i].ChildNodes.Where(n => n.Name == "a").Select(n => n.InnerText).ToArray();
                        }
                        #region Power Pond
                        if(cardInformation.CardName == "Power Bond")
                        {
                            var array = cardSearchCategories["Summoning Categories"];
                            Array.Resize(ref array, cardSearchCategories["Summoning Categories"].Length+1);
                            array[array.Length - 1] = "Special Summons from your Extra Deck";
                            cardSearchCategories["Summoning Categories"] = array;
                        }
                        #endregion
                        cardInformation.CardSearchCategories = cardSearchCategories;
                    }
                }

                xpath = "//th[@class='cardtablerowheader' and contains(text(),'Number')]/../td[@class='cardtablerowdata']";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.CardNumber = node.InnerText.Trim();

                xpath = "//td[@class='cardtable-cardimage']//img";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.ImageThumbnailUrl = node.Attributes["src"].Value;

                xpath = "//td[@class='cardtable-cardimage']//a";
                node = root.SelectSingleNode(xpath);
                if (node != null)
                    cardInformation.ImageUrl = node.Attributes["href"].Value;
                
                cardInformation.Url = url;
                return cardInformation;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
            return new CardInformation();
        }

        public static List<CardInformation> DownloadListCard(ref string url, int pageIndex, bool isReturnAll = false)
        {
            try
            {
                HtmlDocument document;
                string savedFileName = string.Format("Temp\\ListCard\\ListCard{0}.html",pageIndex);
                if (!File.Exists(savedFileName))
                {
                    HtmlWeb web = new HtmlWeb();
                    document = web.Load(url);
                    if (!Directory.Exists("Temp\\ListCard"))
                        Directory.CreateDirectory("Temp\\ListCard");
                    File.WriteAllText(savedFileName, document.DocumentNode.InnerHtml);
                }
                else
                {
                    document = new HtmlDocument();
                    document.Load(savedFileName);
                }
                HtmlNode root = document.DocumentNode;
                string xpath = "//div[@id='mw-pages']//a";
                List<CardInformation> cardInfos = new List<CardInformation>();
                HtmlNodeCollection nodeCollection = root.SelectNodes(xpath);
                if(nodeCollection!=null)
                {
                    foreach (HtmlNode htmlNode in nodeCollection)
                    {
                        CardInformation cardInformation = new CardInformation();
                        cardInformation.CardName = htmlNode.Attributes["title"].Value.Trim().Replace("&quot;","");
                        cardInformation.Url = string.Format("http://yugioh.wikia.com{0}", htmlNode.Attributes["href"].Value.Trim());
                        cardInfos.Add(cardInformation);
                    }
                }
                xpath = "(//div[@class='pagingLinks']//span[@class='nextLink']//a)[1]";
                HtmlNode node = root.SelectSingleNode(xpath);
                url = (node != null) ? string.Format("http://yugioh.wikia.com{0}", node.Attributes["href"].Value.Trim().Replace("amp;","")) : string.Empty;
                if(isReturnAll && !string.IsNullOrEmpty(url))
                {
                    pageIndex++;
                    var result = DownloadListCard(ref url, pageIndex,true);
                    cardInfos.AddRange(result);
                }
                return cardInfos;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
            return new List<CardInformation>();
        }

        public static PageUrl DownloadPageUrl(string url, bool isOCG)
        {
            try
            {
                string folderPath = isOCG ? "Temp/OCG/PageUrls/" : "Temp/TCG/PageUrls/";
                string filename = Path.GetFileNameWithoutExtension(url) + ".html";
                string path = folderPath + filename;
                
                HtmlDocument document;
                if(File.Exists(path))
                {
                    document = new HtmlDocument();
                    document.Load(path);
                }
                else
                {
                    HtmlWeb web = new HtmlWeb();
                    document = web.Load(url);
                    if (!Directory.Exists(folderPath))
                        Directory.CreateDirectory(folderPath);
                    File.WriteAllText(path,document.DocumentNode.InnerHtml);
                }
                string xpath = "(//div[@class='pagingLinks']//span[@class='nextLink']//a)[1]";
                var rootNode = document.DocumentNode;
                var node = rootNode.SelectSingleNode(xpath);
                string nextUrl = (node != null) ? string.Format("http://yugioh.wikia.com{0}", node.Attributes["href"].Value.Trim().Replace("amp;", "")) : 
                                              string.Empty;
                xpath = "//div[@id='mw-pages']//a";
                HtmlNodeCollection nodeCollection = rootNode.SelectNodes(xpath);
                List<CardInformation> cardInfos = null;
                if (nodeCollection != null)
                {
                    cardInfos = new List<CardInformation>();
                    foreach (HtmlNode htmlNode in nodeCollection)
                    {
                        CardInformation cardInformation = new CardInformation();
                        cardInformation.CardName = htmlNode.Attributes["title"].Value.Trim().Replace("&quot;", "");
                        cardInformation.Url = string.Format("http://yugioh.wikia.com{0}", htmlNode.Attributes["href"].Value.Trim());
                        cardInfos.Add(cardInformation);
                    }
                }
                PageUrl pageUrl = new PageUrl
                                 {
                                     Name = filename,
                                     Path = path,
                                     Url = url,
                                     NextUrl = nextUrl,
                                     CardInformations = cardInfos
                                 };
                return pageUrl;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
            return null;
        }

        public static List<PageUrl> DownloadListPageUrl(string url, bool isOCG = true)
        {
            try
            {
                List<PageUrl> pageUrls = new List<PageUrl>();
                PageUrl pageUrl = DownloadPageUrl(url,isOCG);
                while (pageUrl != null)
                {
                    pageUrls.Add(pageUrl);
                    if(!string.IsNullOrEmpty(pageUrl.NextUrl))
                        pageUrl = DownloadPageUrl(pageUrl.NextUrl,isOCG);
                    else
                        break;
                }
                return pageUrls;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
            return null;
        }

        public static bool SaveDataToFile(object obj,string path,string filename)
        {
            try
            {
                if (!Directory.Exists(path))
                    Directory.CreateDirectory(path);
                FileStream fileStream = new FileStream(Path.Combine(path,filename), FileMode.OpenOrCreate);
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(fileStream, obj);
                fileStream.Close();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public static object GetDataFromFile(string path)
        {
            try
            {
                FileStream fileStream = new FileStream(path, FileMode.Open);
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                object obj = binaryFormatter.Deserialize(fileStream);
                fileStream.Close();
                return obj;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        
    }
}